/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


/*************************************************************************
* File: data.hpp
*************************************************************************
*\author: starlove
*************************************************************************/

#ifndef __CC_PYTHON_DATA_H__
#define __CC_PYTHON_DATA_H__

#include "codec.hpp"
#include "buffer.hpp"
#include "memory.hpp"
#include "singleton.hpp"
#include <python_import_macro.h>

NS_CCPY_BEGIN

	class Dict;
	class List;

	/// header, data, reference_data*, param
	typedef bool (*DICT_ITERATOR_FUNC)(size_t index, size_t total, const HASH_KEY *, LPKEYVALUE_DATA_NODE, void *, void *, void *);
	typedef bool (*LIST_ITERATOR_FUNC)(size_t index, size_t total, LPDATA_NODE, void *, void *, void *);

	template <typename T>
	DATA_TP GetDataType()
	{
		if(typeid(T) == typeid(char)) return DTP_INT;
		if(typeid(T) == typeid(short)) return DTP_INT;
		if(typeid(T) == typeid(int)) return DTP_INT;
		if(typeid(T) == typeid(INT64)) return DTP_INT64;
		if(typeid(T) == typeid(float)) return DTP_FLOAT;
		if(typeid(T) == typeid(bool)) return DTP_BOOL;
		if(typeid(T) == typeid(double)) return DTP_DOUBLE;
		if(typeid(T) == typeid(STR)) return DTP_STR;
		if(typeid(T) == typeid(Dict *)) return DTP_MAP;
		if(typeid(T) == typeid(List *)) return DTP_LIST;

		return DTP_NULL;
	};

	typedef CURRENT_HASH_MAP<HASH_KEY, IData *, HASH> DICT_COMPEXDATA_CACHE;
	typedef CURRENT_HASH_MAP<void *, IData *> LIST_COMPEXDATA_CACHE;

	class Dict : public IData
	{
	public:
		~Dict();
		char GetDataType() { return DTP_MAP; };
		bool Load(CBufferReader *);
		bool Dump(IAutoExpandBuffer *);

		/// size
		size_t Size() { return m_kvs.size(); };

		/// set
		void __Set(char, void *, char, void *, size_t);

		template<typename TK, typename T>
		void Set(TK k, T v, size_t s = 0)
		{
			if(cocos2dpython::GetDataType<T>() == DTP_NULL)
				return;
			__Set( cocos2dpython::GetDataType<TK>(), &k, cocos2dpython::GetDataType<T>(), &v, (s == 0 ? sizeof(v) : s) );
		};

		template<typename TK, typename T>
		void SetPtr(TK k, T *v, size_t s = 0)
		{
			__Set( cocos2dpython::GetDataType<TK>(), &k, DTP_MEMORY, &v, (s == 0 ? sizeof(T) : s) );
		};

		/// get
		void *__Get(char, void *, size_t &);
		IData *__GetData(char, void *);

		template<typename TK, typename T>
		T Get(TK k, T def) {
			size_t s = 0;
			void *p = __Get(cocos2dpython::GetDataType<TK>(), &k, s);
			if(p){
				if(cocos2dpython::GetDataType<T>() == DTP_STR)
				{
					return def;
				}
				return *((T *)p);}
			return def;
		}
		template<typename TK, typename T>
		T GetPtr(TK k, T def = NULL, size_t *size = NULL) {
			size_t s = 0;
			void *p = __Get(cocos2dpython::GetDataType<TK>(), &k, s);
			if(size)
				*size = s;
			if(p){
				return reinterpret_cast<T>(p);}
			return def;
		}
		template<typename TK, typename T>
		T *GetData(TK k) {
			return (T *)__GetData(cocos2dpython::GetDataType<TK>(), &k);
		}
		
		template<typename TK>
		const char *GetCString(TK k, const char *def = "")
		{
			static std::string str = "";
			size_t ss = 0;
			const char *p = GetPtr<TK, const char *>(k, def, &ss);
			char *s = new char[ss + 1];
			memcpy(s, p, ss);
			s[ss] = 0;
			str = s;
			delete[] s;
			return str.c_str();
		}

		/// remove
		void __Remove(char, void *);
		template <typename TK>
		void Remove(TK k) { __Remove(cocos2dpython::GetDataType<TK>(), &k); }

		size_t Each(DICT_ITERATOR_FUNC, void *);
		
		void Discard(){ m_buffer.Discard(); };
	private:
		///utils
		static void BuildHashKey(HASH_KEY *, char, void *, size_t * = NULL);
		int CacheData(const void *, size_t);

	private:
		CODEC_BUFFER m_buffer;
		DICT_COMPEXDATA_CACHE m_cache_dicts;
		DICT_COMPEXDATA_CACHE m_cache_lists;
		CURRENT_HASH_MAP<HASH_KEY, KEYVALUE_DATA_NODE, HASH> m_kvs;
	};

	class List : public IData
	{
	public:
		~List();
		char GetDataType() { return DTP_LIST; };
		bool Load(CBufferReader *);
		bool Dump(IAutoExpandBuffer *);

		/// size
		size_t Size() { return m_vs.size(); };

		/// add
		void __Add(char, void *, size_t);
		template<typename T>
		void Add(T v, size_t s = 0) { __Add(cocos2dpython::GetDataType<T>(), &v, s == 0 ? sizeof(v) : s); };

		template<typename T>
		void AddPtr(T *v, size_t s = 0) { __Add(DTP_MEMORY, &v, s == 0 ? sizeof(T) : s); };

		/// get
		void *__Get(size_t, size_t &, void ** = NULL);
		IData *__GetData(size_t);

		template<typename T>
		T Get(size_t index, T def) {
			size_t s = 0;
			void *p = __Get(index, s);
			if(p){
				if(cocos2dpython::GetDataType<T>() == DTP_STR)
				{
					return def;
				}
				return *((T *)p);}
			return def;
		}

		template<typename T>
		T GetPtr(size_t index, T def = NULL) {
			size_t s = 0;
			void *p = __Get(index, s);
			if(p){
				return reinterpret_cast<T>(p);}
			return def;
		}
		template<typename T>
		T *GetData(size_t index) {
			return (T *)__GetData(index);
		}

		/// remove
		void __Remove(size_t);
		void Remove(size_t st) { __Remove(st); }

		size_t Each(LIST_ITERATOR_FUNC, void *);
		void Discard(){ m_buffer.Discard(); };
	private:
		///utils
		int CacheData(const void *, size_t);

	private:
		std::vector<DATA_NODE> m_vs;
		CODEC_BUFFER m_buffer;
		LIST_COMPEXDATA_CACHE m_cache_dicts;
		LIST_COMPEXDATA_CACHE m_cache_lists;
	};

	class __tmp_cache
	{
		DECLARE_SINGLETON(__tmp_cache)
	public:
		void Initialize(){};
		void Destroy(){};

		Dict *AllocDict()
		{
			return new Dict;
		}

		List *AllocList()
		{
			return new List;
		}

		void FreeDict(IData *p)
		{
			Dict *dt = dynamic_cast<Dict *>(p);
			if(dt)
				delete dt;
		}

		void FreeList(IData *p)
		{
			List *dt = dynamic_cast<List *>(p);
			if(dt)
				delete dt;
		}
	};

NS_CCPY_END

#endif // !__CC_PYTHON_DATA_H__
